/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.bef.core.session.distributed;

import com.inspur.edp.bef.api.BefRtBeanUtil;
import com.inspur.edp.bef.core.action.modify.ModifyAction;
import com.inspur.edp.bef.core.be.BusinessEntity;
import com.inspur.edp.bef.core.lcp.CoreLcpFactory;
import com.inspur.edp.bef.core.lcp.StandardLcp;
import com.inspur.edp.bef.core.session.BEFuncSessionBase;
import com.inspur.edp.bef.core.session.FuncSession;
import com.inspur.edp.bef.core.session.FuncSessionUtil;
import com.inspur.edp.cef.api.session.ICefSession;
import com.inspur.edp.cef.entity.accessor.base.IAccessor;
import com.inspur.edp.cef.entity.changeset.AddChangeDetail;
import com.inspur.edp.cef.entity.changeset.ChangeType;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.changeset.ModifyChangeDetail;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.commonmodel.core.session.SessionItemRecover;
import com.inspur.edp.commonmodel.core.session.distributed.dac.FuncSessionItemIncrement;
import com.inspur.edp.commonmodel.core.variable.VarBufferManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import lombok.SneakyThrows;
import org.springframework.util.StringUtils;

public class SessionItemRecoverImpl implements SessionItemRecover<BEFuncSessionBase> {

  @Override
  public void reset(BEFuncSessionBase sessionItem) {
    sessionItem.getChanges().clear();
    sessionItem.getBuffers().clear();
    sessionItem.getBizEntityCacheManager().clearEntities();
    sessionItem.getLockServiceItem().reset();
  }

  @Override
  public BEFuncSessionBase buildSessionItem(ICefSession session, String configId) {
    StandardLcp lcp = (StandardLcp) ((CoreLcpFactory) BefRtBeanUtil.getLcpFactory())
        .recoverLcp(configId);
    lcp.setCurrentSession((FuncSession) session);
    return lcp.createSessionItem((FuncSession) session);
  }

  @Override
  public void incrementBeforeReset(BEFuncSessionBase item, FuncSessionItemIncrement increment) {
    increment4Variable(item, increment);
  }

  @Override
  public void incrementAfterReset(BEFuncSessionBase item, FuncSessionItemIncrement increment) {
    increment4Variable(item, increment);

    increment4Locks(item, increment);
    increment4ChangeBuffer(item, increment);
    increment4WeakVariables(item, increment);
  }

  private void increment4WeakVariables(BEFuncSessionBase item, FuncSessionItemIncrement increment) {
    if(increment.getCustoms() == null){
      return;
    }
    String repoVar = increment.getCustoms().get("RepoVar");
    if(repoVar == null){
      return;
    }
    item.getRepositoryVariables().clear();
    if(!repoVar.equals("")){
      item.getRepositoryVariables().putAll(FuncSessionUtil.convertFromBinary(repoVar));
    }
  }

  private void increment4Variable(BEFuncSessionBase item, FuncSessionItemIncrement increment) {
    if (StringUtils.isEmpty(increment.getVarChangeString())) {
      return;
    }
    VarBufferManager manager = item.getVarBufferManager();
    IChangeDetail varChange = manager.getVarManager()
        .deserializeChange(increment.getVarChangeString());
    //TODO: 调试看看只读data变不变
    ((IAccessor) manager.getCurrentData()).acceptChange(varChange);
  }

  private void increment4ChangeBuffer(BEFuncSessionBase item, FuncSessionItemIncrement increment) {
    if (!StringUtils.isEmpty(increment.getChangeDetailString())) {
      increment.setChangeDetails(
          item.getBEManager().deserializeChanges(increment.getChangeDetailString()));
    }

    if (increment.getChangeDetails() != null && !increment.getChangeDetails().isEmpty()) {
      retrieve(item, increment.getChangeDetails());
      for (IChangeDetail change : increment.getChangeDetails()) {
        if(change.getChangeType() == ChangeType.Added) {
          IAccessor acc = item.getBufferChangeManager().createCurrentBuffer(((AddChangeDetail)change).getEntityData());
          ((AddChangeDetail) change).setEntityData((IEntityData) acc);
          ((BusinessEntity)item.getBizEntityCacheManager().getEntity(change.getDataID())).getBEContext().startChangeListener();
        }
        BusinessEntity entity = (BusinessEntity) item.getBEManager().getEntity(change.getDataID());
        ModifyAction.setNestedValue(change, entity.getBEContext());
        convertChange(entity, change);
        entity.appendTempCurrentChange(change);
        entity.getBEContext().acceptChanges();
      }
    }
  }

  private static void convertChange(BusinessEntity entity, IChangeDetail change) {
    if(!(change instanceof ModifyChangeDetail)) {
      return;
    }
    ModifyChangeDetail modifyChg = (ModifyChangeDetail) change;
    if(modifyChg.getChildChanges() == null) {
      return;
    }

    for (Entry<String, Map<String, IChangeDetail>> pair : modifyChg.getChildChanges().entrySet()) {
      if(pair.getValue() == null || pair.getValue().isEmpty()) {
        continue;
      }
      for (Entry<String, IChangeDetail> changePair : pair.getValue().entrySet()) {
        if(changePair.getValue() instanceof ModifyChangeDetail) {
          convertChange(entity, changePair.getValue());
        } else if(changePair.getValue() instanceof AddChangeDetail){
          AddChangeDetail addChange = (AddChangeDetail) changePair.getValue();
          if(addChange.getEntityData() == null || addChange.getEntityData() instanceof IAccessor) {
            continue;
          }
          addChange.setEntityData(entity.createChildAccessor(pair.getKey(), addChange.getEntityData()));
        }
      }
    }
  }

  @SneakyThrows
  private void retrieve(BEFuncSessionBase item, List<IChangeDetail> changes) {
    List<String> ids = new ArrayList<>(changes.size());
    for (IChangeDetail change : changes) {
      if (change.getChangeType() == ChangeType.Added) {
        continue;
      }
      IChangeDetail tranChange = item.getBufferChangeManager()
          .getTransactionChange(change.getDataID());
      if (tranChange != null && tranChange.getChangeType() == ChangeType.Added) {
        continue;
      }
      BusinessEntity entity = (BusinessEntity) item.getBizEntityCacheManager().getEntity(change.getDataID());
      if(entity.getBEContext().getOriginalData() == null) {
        ids.add(change.getDataID());
      }
    }
    if (!ids.isEmpty()) {
      //TODO: retrieveParam
      List<IEntityData> datas = item.getBEManager().getRepository().retrieve(ids);
      if(datas != null) {
        for (IEntityData data : datas) {
          if (data == null) {
            continue;
          }
          ((BusinessEntity) item.getBizEntityCacheManager().getEntity(data.getID())).loadData(data);
        }
      }
    }
  }

  private void increment4Locks(BEFuncSessionBase item, FuncSessionItemIncrement increment) {
    item.getLockServiceItem().recover(increment);
  }
}
